package org.gwt.jaxen.gwtxml;

/*
 * $Header: /home/projects/jaxen/scm/jaxen/src/java/main/org.gwt/jaxen/jdom/DocumentNavigator.java,v 1.30 2006/02/05 21:47:42 elharo Exp $
 * $Revision: 1.30 $
 * $Date: 2006/02/05 21:47:42 $
 *
 * ====================================================================
 *
 * Copyright 2000-2005 bob mcwhirter & James Strachan.
 * All rights reserved.
 *
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 
 *   * Neither the name of the Jaxen Project nor the names of its
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ====================================================================
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Jaxen Project and was originally
 * created by bob mcwhirter <bob@werken.com> and
 * James Strachan <jstrachan@apache.org>.  For more information on the
 * Jaxen Project, please see <http://www.jaxen.org/>.
 *
 * $Id: DocumentNavigator.java,v 1.30 2006/02/05 21:47:42 elharo Exp $
*/

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import org.gwt.jaxen.DefaultNavigator;
import org.gwt.jaxen.FunctionCallException;
import org.gwt.jaxen.JaxenConstants;
import org.gwt.jaxen.NamedAccessNavigator;
import org.gwt.jaxen.Navigator;
import org.gwt.jaxen.XPath;
import org.gwt.jaxen.saxpath.SAXPathException;
import org.gwt.jaxen.util.SingleObjectIterator;
//import org.jdom.Attribute;
//import org.jdom.CDATA;
//import org.jdom.Comment;
//import org.jdom.Document;
//import org.jdom.Element;
//import org.jdom.Namespace;
//import org.jdom.ProcessingInstruction;
//import org.jdom.Text;
//import org.jdom.input.SAXBuilder;
//import org.wsmo.gwt.common.IRI;


import com.google.gwt.xml.client.Attr;
import com.google.gwt.xml.client.CDATASection;
import com.google.gwt.xml.client.CharacterData;
import com.google.gwt.xml.client.Comment;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Element;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.ProcessingInstruction;
import com.google.gwt.xml.client.Text;


/** 
 * Interface for navigating around the JDOM object model.
 *
 * <p>
 * This class is not intended for direct usage, but is
 * used by the Jaxen engine during evaluation.
 * </p>
 *
 * @see XPath
 *
 * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
 * @author Stephen Colebourne
 */
public class DocumentNavigator extends DefaultNavigator implements NamedAccessNavigator
{
    /** Singleton implementation.
     */
    private static class Singleton
    {
        /** Singleton instance.
         */
        private static DocumentNavigator instance = new DocumentNavigator();
    }

    public static Navigator getInstance()
    {
        return Singleton.instance;
    }

    public boolean isElement(Object obj)
    {
        return (obj instanceof Element);
    }

    public boolean isComment(Object obj)
    {
        return (obj instanceof Comment);
    }

    public boolean isText(Object obj)
    {
    	return ( (obj instanceof Text)
                ||
                (obj instanceof CDATASection) );
    }

    public boolean isAttribute(Object obj)
    {	
        return (obj instanceof Attr);
    }

    public boolean isProcessingInstruction(Object obj)
    {
        return (obj instanceof ProcessingInstruction);
    }

    public boolean isDocument(Object obj)
    {
        return (obj instanceof Document);
    }

    public boolean isNamespace(Object obj)
    {
//        return obj instanceof Namespace || obj instanceof XPathNamespace;
    	return obj instanceof String || obj instanceof XPathNamespace;
    }

    public String getElementName(Object obj)
    {
        Element elem = (Element) obj;

        return elem.getNodeName();
    }

    public String getElementNamespaceUri(Object obj)
    {
        Element elem = (Element) obj;

        String uri = elem.getNamespaceURI();
        if ( uri != null && uri.length() == 0 ) 
            return null;
        else
            return uri;
    }

    public String getAttributeName(Object obj)
    {
        Attr attr = (Attr) obj;

        return attr.getName();
    }

    public String getAttributeNamespaceUri(Object obj)
    {
    	Attr attr = (Attr) obj;

        String uri = attr.getNamespaceURI();
        if ( uri != null && uri.length() == 0 ) 
            return null;
        else
            return uri;
    }

    public Iterator getChildAxisIterator(Object contextNode)
    {
//        if ( contextNode instanceof Element )
//        {
//            return ((Element)contextNode).getContent().iterator();
//        }
//        else if ( contextNode instanceof Document )
//        {
//            return ((Document)contextNode).getContent().iterator();
//        }
//
//        return JaxenConstants.EMPTY_ITERATOR;

    	if(!(contextNode instanceof Element || contextNode instanceof Document)){
    		return JaxenConstants.EMPTY_ITERATOR;
    	}
    	Node el = (Node)contextNode;
    	Vector v = new Vector();
    	for (int i = 0; i < el.getChildNodes().getLength(); i++) {
			v.add(el.getChildNodes().item(i));
		}
        return v.iterator();
    }

    /**
     * Retrieves an <code>Iterator</code> over the child elements that
     * match the supplied name.
     *
     * @param contextNode  the origin context node
     * @param localName  the local name of the children to return, always present
     * @param namespacePrefix  the prefix of the namespace of the children to return
     * @param namespaceURI  the uri of the namespace of the children to return
     * @return an Iterator that traverses the named children, or null if none
     */
    public Iterator getChildAxisIterator(
            Object contextNode, String localName, String namespacePrefix, String namespaceURI) {

//        if ( contextNode instanceof Element ) {
//            Element node = (Element) contextNode;
//            if (namespaceURI == null) {
//                return node.getChildren(localName).iterator();
//            }
//            return node.getChildren(localName, Namespace.getNamespace(namespacePrefix, namespaceURI)).iterator();
//        }
//        if ( contextNode instanceof Document ) {
//            Document node = (Document) contextNode;
//            
//            Element el = node.getRootElement();
//            if (el.getName().equals(localName) == false) {
//                return JaxenConstants.EMPTY_ITERATOR;
//            }
//            if (namespaceURI != null) {
//                if (Namespace.getNamespace(namespacePrefix, namespaceURI).equals(el.getNamespace()) == false) {
//                    return JaxenConstants.EMPTY_ITERATOR;
//                }
//            }
//            return new SingleObjectIterator(el);
//        }
//
//        return JaxenConstants.EMPTY_ITERATOR;
    
    	
      if (contextNode instanceof Element) {
			Element node = (Element) contextNode;
			//if (namespaceURI == null) {
			NodeList n =null;
				n = node.getElementsByTagName(localName);
			//}
//			return node.getChildren(localName,
//					Namespace.getNamespace(namespacePrefix, namespaceURI))
//					.iterator();
				ArrayList a = new ArrayList();
		    	for (int i = 0; i < n.getLength(); i++) {
					a.add(n.item(i));
				}
		        return a.iterator();
		}
		if (contextNode instanceof Document) {
			Document node = (Document) contextNode;

			Element el = node.getDocumentElement();
			if (el.getNodeName().equals(localName) == false) {
				return JaxenConstants.EMPTY_ITERATOR;
			}
//			if (namespaceURI != null) {
//				if (Namespace.getNamespace(namespacePrefix, namespaceURI)
//						.equals(el.getNamespace()) == false) {
//					return JaxenConstants.EMPTY_ITERATOR;
//				}
//			}
			return new SingleObjectIterator(el);
		}

		return JaxenConstants.EMPTY_ITERATOR;
    	
    	
    	
    }
    
    public Iterator getNamespaceAxisIterator(Object contextNode)
    {
        if ( ! ( contextNode instanceof Element ) )
        {
            return JaxenConstants.EMPTY_ITERATOR;
        }

        Element elem = (Element) contextNode;

        Map nsMap = new HashMap();

        Element current = elem;

        while ( current != null ) {
        
//            Namespace ns = current.getNamespace();
//            
//            if ( ns != Namespace.NO_NAMESPACE ) {
//                if ( !nsMap.containsKey(ns.getPrefix()) )
//                    nsMap.put( ns.getPrefix(), new XPathNamespace(elem, ns) );
//            }
//        
//            Iterator additional = current.getAdditionalNamespaces().iterator();
//
//            while ( additional.hasNext() ) {
//
//                ns = (Namespace)additional.next();
//                if ( !nsMap.containsKey(ns.getPrefix()) )
//                    nsMap.put( ns.getPrefix(), new XPathNamespace(elem, ns) );
//            }
//
//            if (current.getParent() instanceof Element) {
//                current = (Element)current.getParent();
//            } else {
//                current = null;
//            }
        	
        	String ns = current.getNamespaceURI();
        	
        	if ( !ns.equals("") ) {
              if ( !nsMap.containsKey(current.getPrefix()) )
                  nsMap.put( current.getPrefix(), new XPathNamespace(elem, ns) );
        	}

            if (current.getParentNode() instanceof Element) {
                current = (Element)current.getParentNode();
            } else {
                current = null;
            }
        }

        //nsMap.put( "xml", new XPathNamespace(elem, Namespace.XML_NAMESPACE) );

        return nsMap.values().iterator();
    }

    public Iterator getParentAxisIterator(Object contextNode)
    {
        Object parent = null;

        if ( contextNode instanceof Document )
        {
            return JaxenConstants.EMPTY_ITERATOR;
        }
        else if ( contextNode instanceof Element )
        {
            parent = ((Element)contextNode).getParentNode();

//            if ( parent == null )
//            {
//                if ( ((Element)contextNode).isRootElement() )
//                {
//                    parent = ((Element)contextNode).getDocument();
//                }
//            }
        }
        else if ( contextNode instanceof Attr )
        {
            parent = ((Attr)contextNode).getParentNode();
        }
        else if ( contextNode instanceof XPathNamespace )
        {
            parent = ((XPathNamespace)contextNode).getGWTElement();
        }
//        else if ( contextNode instanceof ProcessingInstruction )
//        {
//            parent = ((ProcessingInstruction)contextNode).getParent();
//        }
//        else if ( contextNode instanceof Comment )
//        {
//            parent = ((Comment)contextNode).getParent();
//        }
//        else if ( contextNode instanceof Text )
//        {
//            parent = ((Text)contextNode).getParent();
//        }
        else if ( contextNode instanceof Node )
          {
              parent = ((Node)contextNode).getParentNode();
          }
        
        if ( parent != null )
        {
            return new SingleObjectIterator( parent );
        }

        return JaxenConstants.EMPTY_ITERATOR;
    }

    public Iterator getAttributeAxisIterator(Object contextNode)
    {
        if ( ! ( contextNode instanceof Element ) )
        {
            return JaxenConstants.EMPTY_ITERATOR;
        }

        Element el = (Element) contextNode;

    	Vector v = new Vector();
    	for (int i = 0; i < el.getAttributes().getLength(); i++) {
			v.add(el.getAttributes().item(i));
		}
        return v.iterator();

    }

    /**
     * Retrieves an <code>Iterator</code> over the attribute elements that
     * match the supplied name.
     *
     * @param contextNode  the origin context node
     * @param localName  the local name of the attributes to return, always present
     * @param namespacePrefix  the prefix of the namespace of the attributes to return
     * @param namespaceURI  the URI of the namespace of the attributes to return
     * @return an Iterator that traverses the named attributes, not null
     */
    public Iterator getAttributeAxisIterator(
            Object contextNode, String localName, String namespacePrefix, String namespaceURI) {

        if ( contextNode instanceof Element ) {
            Element node = (Element) contextNode;
//            Namespace namespace = (namespaceURI == null ? Namespace.NO_NAMESPACE : 
//                                    Namespace.getNamespace(namespacePrefix, namespaceURI));
//            Attribute attr = node.getAttribute(localName, namespace);
            Attr attr = node.getAttributeNode(localName);
            if (attr != null) {
                return new SingleObjectIterator(attr);
            }
        }
        return JaxenConstants.EMPTY_ITERATOR;
    }

    /** Returns a parsed form of the given XPath string, which will be suitable
     *  for queries on JDOM documents.
     */
    public XPath parseXPath (String xpath) throws SAXPathException
    {
        return new GWTXPath(xpath);
    }

    public Object getDocumentNode(Object contextNode)
    {
        if ( contextNode instanceof Document )
        {
            return contextNode;
        }

        Node elem = (Node) contextNode;

        return elem.getOwnerDocument();
    }

    public String getElementQName(Object obj)
    {
        Element elem = (Element) obj;

        String prefix = elem.getPrefix();

        if ( prefix == null || prefix.length() == 0 )
        {
            return elem.getNodeName();
        }

        return prefix + ":" + elem.getNodeName();
    }

    public String getAttributeQName(Object obj)
    {
        Attr attr = (Attr) obj;

        String prefix = attr.getPrefix();

        if ( prefix == null || prefix.length() == 0 )
        {
            return attr.getNodeName();
        }

        return prefix + ":" + attr.getNodeName();
    }

    public String getNamespaceStringValue(Object obj)
    {
        if (obj instanceof String) {
        	return (String)obj;
        } else {

            XPathNamespace ns = (XPathNamespace) obj;
            return ns.getGWTNamespace();
        }
        
    }

    public String getNamespacePrefix(Object obj)
    {
//        if (obj instanceof Namespace) {
//
//            Namespace ns = (Namespace) obj;
//            return ns.getPrefix();
//        } else {
//
//            XPathNamespace ns = (XPathNamespace) obj;
//            return ns.getJDOMNamespace().getPrefix();
//        }
    	return null;
    }

    public String getTextStringValue(Object obj)
    {
//        if ( obj instanceof Text )
//        {
//            return ((Text)obj).getText();
//        }
//
//        if ( obj instanceof CDATA )
//        {
//            return ((CDATA)obj).getText();
//        }
    	
      if ( obj instanceof Text )
      {
          return ((Text)obj).getData();
      }

      if ( obj instanceof CDATASection )
      {
          return ((CDATASection)obj).getData();
      }

        return "";
    }

    public String getAttributeStringValue(Object obj)
    {
        Attr attr = (Attr) obj;

        return attr.getValue();
    }

    public String getElementStringValue(Object obj)
    {
        Element elem = (Element) obj;

        StringBuffer buf = new StringBuffer();
         
//        List     content     = elem.getContent();
//        Iterator contentIter = content.iterator();
//        Object   each        = null;

        for (int i = 0; i < elem.getChildNodes().getLength(); i++) {
			Node n = elem.getChildNodes().item(i);
			switch (n.getNodeType()) {
			case Node.TEXT_NODE:
				buf.append(((CharacterData)n).getData());
				break;
				
			case Node.CDATA_SECTION_NODE:
				buf.append(((CharacterData)n).getData());
				break;
			
			case Node.ELEMENT_NODE:
				buf.append(getElementStringValue(n));
				
			default:
				break;
			}
		}
        
//        while ( contentIter.hasNext() )
//        {
//            each = contentIter.next();
//
//            if ( each instanceof Text )
//            {
//                buf.append( ((Text)each).getText() );
//            }
//            else if ( each instanceof CDATA )
//            {
//                buf.append( ((CDATA)each).getText() );
//            }
//            else if ( each instanceof Element )
//            {
//                buf.append( getElementStringValue( each ) );
//            }
//        }

        return buf.toString();
    }

//    public String getProcessingInstructionTarget(Object obj)
//    {
//        ProcessingInstruction pi = (ProcessingInstruction) obj;
//
//        return pi.getTarget();
//    }

//    public String getProcessingInstructionData(Object obj)
//    {
//        ProcessingInstruction pi = (ProcessingInstruction) obj;
//
//        return pi.getData();
//    }

    public String getCommentStringValue(Object obj)
    {
        Comment cmt = (Comment) obj;

        return cmt.getData();
    }

//    public String translateNamespacePrefixToUri(String prefix, Object context)
//    {
//        Element element = null;
//        if ( context instanceof Element ) 
//        {
//            element = (Element) context;
//        }
//        else if ( context instanceof CharacterData )
//        {
//            element = (Element)((CharacterData)context).getParentNode();
//        }
//        else if ( context instanceof Attr )
//        {
//            element = (Element)((Attr)context).getParentNode();
//        }
//        else if ( context instanceof XPathNamespace )
//        {
//            element = ((XPathNamespace)context).getGWTElement();
//        }
//        else if ( context instanceof Comment )
//        {
//            element = (Element)((Comment)context).getParentNode();
//        }
////        else if ( context instanceof ProcessingInstruction )
////        {
////            element = (Element)((ProcessingInstruction)context).getParent();
////        }
//
//        if ( element != null )
//        {
//            String namespace = element.getNamespaceURI();
//
//            if ( namespace != null ) 
//            {
//                return namespace;
//            }
//        }
//        return null;
//    }

//    public Object getDocument(String url) throws FunctionCallException
//    {
//    	final Document doc;
//    	
//        try
//        {
//        	HTTPRequest.asyncGet(url, new ResponseTextHandler(){
//
//				public void onCompletion(String responseText) {
//					doc = XMLParser.parse(responseText);
//				}
//			});
//        	(new Timer(){
//
//				public void run() {
//					throw new Exception("Timeout for fetching url");
//				}
//        		
//        	}).schedule(5000);
//        	while(doc==null){}
//
//            return doc;
//        }
//        catch (Exception e)
//        {
//            throw new FunctionCallException( e.getMessage() );
//        }
//    }
}
